import re
import testExpr
from graphviz import Digraph
import time
import random
# PROG→BLOCK
# BLOCK→{  DECLS  STMTS  }
# DECLS→DECLS  DECL | empty 
# DECL→TYPE  NAMES  ;
# TYPE→int
# NAMES→NAMES  ,  NAME | NAME NAME→id
# STMTS→STMTS  STMT | empty 
# STMT→id  = EXPR ;
# STMT→if  (  REL )  STMT
# STMT→if  (  REL )  STMT  else  STMT
# STMT→while  (  REL )  STMT
# STMT→BLOCK
#
# REL→EXPR  ROP  EXPR
# ROP→ > | >= | < | <= | == | !=

#关键词列表
keywords=['int', 'long', 'short', 'float', 'double', 'char', 'unsigned', 'signed', 'const', 'void', 'volatile', 'enum', 'struct', 'union', 'if', 'else', 'goto', 'switch', 'case', 'do', 'while', 'for', 'continue', 'break', 'return', 'default', 'typedef', 'auto', 'register', 'extern', 'static', 'sizeof']
ROp=[">=","<=","==","!=",">","<"]


#drawTree()绘制树 并打印
#tree 表示树的字典结构 
def drawTree(tree):
  dot = Digraph(name="Tree", format="png")
  drawTreePoint(tree,dot,-1,getRandId())
  dot.render(filename="Tree")

#getRandId()生成随机节点Id
def getRandId():
  timeStr= str(int(round(time.time() * 1000)))[-8:]
  randomStr=""
  for i in range(7):
    randomStr+=str(random.randint(1,9))
  return timeStr+randomStr

#drawTreePoint()绘制树节点以及连线
#tree 表示树的字典结构 
#dot  绘图对象  初始传入为None
#parentId 树父亲节点的编号 初代为-1
#currentId 当前节点的编号 时间戳随机ID 唯一性
def drawTreePoint(tree,dot,parentId,currentId):
  if parentId == -1:
      dot.node(currentId,tree["root"])
      for key in sorted(tree.keys()):
        if key!="root":
          drawTreePoint(tree[key],dot,currentId,getRandId())
      return 
  #1.若空节点 不做操作
  if tree is None:
    return
  #2.若是叶子则绘制叶子节点和边
  if isinstance(tree,str):
    dot.node(currentId,tree)
    dot.edge(parentId,currentId)
    return
  #3.若是子树
  if isinstance(tree, dict):
    #3.1若子树根是空的 则跳过该子树
    if tree["root"] is None:
      return
    #3.2若子树有根 则先绘制根节点 并将根节点与父亲节点连线 并递归调用绘制三个子叶  
    else:
      dot.node(currentId,tree["root"])
      dot.edge(parentId,currentId)
      for key in sorted(tree.keys()):
        if key!="root":
          drawTreePoint(tree[key],dot,currentId,getRandId())
      return



#Decls
#对引入的表达式检测进行二次封装
def testExprBox(expr):
  newExpr=expr.replace(" ","").replace("\n","").replace("\t","")
  retDict=testExpr.testExpr(newExpr)
  return retDict


#extractBlock()去除{}后获取其中并返回剩余代码
#同时也会检测{}是否匹配
#返回结果字典
def extractBlock(code):
  count=0
  if code[:1]!='{':
    return {"status":0,"need":None,"new":None}
  for index,char in enumerate(code):
    if char =="{":
      count+=1
    elif char=="}":
      count-=1
      if count==0:
        return {"status":1,"need":code[1:index],"new":code[index+1:]}
      #说明出现了}{的情况
      if count<0:
        return {"status":0,"need":None,"new":None}
  return {"status":0,"need":None,"new":None}

#extractBreaket() 提取一层括号中的内容
#expr 表达式字符串
# 返回一个结果字典
#  {
#     "status":结果状态(0找到|1没找到),
#     "need":返回括号内的元素(元素字符串|None),
#     "new":返回)后的表达式(表达式字符串|None),
#   }
def extractBreaket(expr):
  count=0
  #1.检测首个是否为(
  if expr[:1] != '(':
    return {"status":0,"need":None,"new":None}
  for index,char in enumerate(expr):
    if char =="(":
      count+=1
    elif char==")":
      count-=1
      if count==0:
        return {"status":1,"need":expr[1:index],"new":expr[index+1:]}
  return {"status":0,"need":None,"new":None}

#testType()检测DECL前方是否有一个type
#line 送入的行语句(已进行)
def testType(line):
  #读取前三个字符必须是int 且int后必须空一格
  if line[:3]=="int" and len(line)>4 and line[3]==" ":
    #由于已经检测了必须的空格 剩下的namelist列表则可以直接
    return {"status":1,"need":line[:3],"new":line[3:].replace(" ",""),"needTree":line[:3]+"  => Type"}  
  else:
    return {"status":0,"need":None,"new":None}


#testName() 检测name 实际为id检测
#name是送入的一个待检测name字段
def testName(name):
  reId=re.match("^[a-zA-z_]\w*", name)
  if reId is not None:
    need=reId.group()
    if need not in keywords and len(need)==len(name):
      return{"status":1,"need":need,"needTree":need+"  => Id"}
  #不属于上述任意情况的 统统报错
  return{"status":0}

#testNameList() 检测nameList 实际为二分检测
#line是送入的一个待检测文本
def testNameList(line):
  needTree={"root":None}
  need=""
  #1.存在,
  if "," in line:
    names=line.split(",")
    #1.2检测一个Name
    retDict1=testName(names[0])
    if not retDict1["status"]:
      return {"status":0}
    need=need+retDict1["need"]+","
    needTree["leafA"]=retDict1["needTree"]
    #1.3 将剩下的字段送入检测NameList
    retDict2=testNameList(line[len(retDict1["need"])+1:])
    if not retDict2["status"]:
      return {"status":0}
    need=need+retDict2["need"]
    needTree["leafB"]=retDict2["needTree"]
    needTree["root"]=need+"  => NameList"
    return {"status":1,"need":need,"needTree":needTree}
  #2.不存在, 
  else:
    #2.1 检测一个Name
    retDict3=testName(line)
    if not retDict3["status"]:
      return {"status":0}
    else:
      need=need+retDict3["need"]
      needTree["leafA"]=retDict3["needTree"]
      needTree["root"]=need+"  => NameList"
      return {"status":1,"need":need,"needTree":needTree}


#检测Decl
def testDecl(line):
  needTree={"root":None}
  need=""
  line=line.strip()
  #1.检测 type是否合法
  retDict1=testType(line)
  if not retDict1["status"]:
    return {"status":0}
  else:
    need=need+retDict1["need"]+" "
    needTree["leafA"]=retDict1["needTree"]
  #2.检测名字清单
  retDict2=testNameList(retDict1["new"])
  if not retDict2["status"]:
    return {"status":0}
  else:
    need=need+retDict2["need"]+";"
    print("[Decl]:"+need)
    needTree["leafB"]=retDict2["needTree"]
    needTree["root"]=need+"  => Decl"
    return {"status":1,"need":need,"needTree":needTree}


def testDecls(code):
  need=""
  new=""
  needTree={"root":None}
  #1.获取一个语法单位
  retDict1=getCodeUnit(code)
  #1.1 检测是否存在语法单位错误
  if not retDict1["status"]:
    return {"status":0}
  #1.2 检测是否为空
  elif retDict1["status"]==3:
    return {"status":1,"need":need,"new":"","needTree":needTree}
  #1.3 检测是否为代码块(若是代码块则将解析前的原样传回)  
  elif retDict1["status"]==2:
    return {"status":1,"need":need,"new":code,"needTree":needTree}
  #1.4 检测为单行语句则传入Decl
  elif retDict1["status"]==1:
    
    #1.4.1 检测Decl
    retDict2=testDecl(retDict1["need"])
    if not retDict2["status"]:
      return {"status":1,"need":need,"new":code,"needTree":needTree}
    need=need+retDict2["need"]
    needTree["leafA"]=retDict2["needTree"]
    #1.4.2 检测Decls
    retDict3=testDecls(retDict1["new"])
    if not retDict3["status"]:
      return {"status":0}
    else:
      need=need+retDict3["need"]
      new=new+retDict3["new"]
      needTree["leafB"]=retDict3["needTree"]
      needTree["root"]=need+"  => Decls"
      return {"status":1,"need":need,"new":new,"needTree":needTree}

#传入当前剩余代码
def testStms(code):
  need=""
  new=""
  needTree={"root":None}
  #1.获取一个语法单位(检错 检空)
  retDict1=getCodeUnit(code)
  #1.1 检测是否存在语法单位错误
  if not retDict1["status"]:
    return {"status":0}
  #1.2 检测是否为空
  elif retDict1["status"]==3:
    return {"status":1,"need":need,"new":"","needTree":needTree}
  
  #2.检测Stm
  retDict2=testStm(code)
  if not retDict2["status"]:
    return {"status":0}
  need=need+retDict2["need"]
  needTree["leafA"]=retDict2["needTree"]
  #3.检测Stms
  retDict3=testStms(retDict2["new"])
  if not retDict3["status"]:
    return {"status":0}
  else:
    need=need+retDict3["need"]
    new=new+retDict3["new"]
    needTree["leafB"]=retDict3["needTree"]
    needTree["root"]=need+"  => Stms"
    return {"status":1,"need":need,"new":new,"needTree":needTree} 

#code 传入当前剩余的代码
def testStm(code):
  need=""
  new=""
  #1.获取一个语法单位
  retDict1=getCodeUnit(code)
  #1.1 检测是否存在语法单位错误
  if not retDict1["status"]:
    return {"status":0}
  #1.2 检测是否为空(empty)
  elif retDict1["status"]==3:
    return {"status":0,"need":need,"new":""}   
  # 1.3 检测是否为纯代码块(即 { 前方不含if/while/else字段的) 
  elif retDict1["status"]==2 and retDict1["before"]=="":
    retDict5=testBlock(retDict1["need"])
    if not retDict5["status"]:
      return {"status":0}
    return {"status":1,"need":retDict5["need"],"new":retDict1["new"],"needTree":retDict5["needTree"]}

  #1.4 检测为单行语句则转入(必定单行语句 赋值语句 检测)
  elif retDict1["status"]==1:  
    #1.4.1 检测是否为赋值
    retDict2=testAssign(retDict1["need"])
    if retDict2["status"]:
      print("[Stm]:"+retDict2["need"])  
      return {"status":1,"need":retDict2["need"],"new":retDict1["new"],"needTree":retDict2["needTree"]}
  #1.5 其余情况下(含代码块且{前有东西的 单行语句但不是赋值) 进行三类模糊语句的检测    
  #1.5.1 while语句检测
  retDict3=testWhile(code)
  if retDict3["status"]:
    print("[Stm]:"+retDict3["need"]) 
    return {"status":1,"need":retDict3["need"],"new":retDict3["new"],"needTree":retDict3["needTree"]}
  #1.5.2 检测是否为if-else
  retDict4=testIfElse(code)
  if retDict4["status"]:
    print("[Stm]:"+retDict4["need"]) 
    return {"status":1,"need":retDict4["need"],"new":retDict4["new"],"needTree":retDict4["needTree"]}
  return {"status":0}

#code:一组已经去掉最外层括号的代码序列
def testBlock(code):
  need=""
  new=""
  needTree={"root":None}
  #1.检测Decls
  retDict1=testDecls(code)
  if not retDict1["status"]:
    return {"status":0}
  else:
    need="{"+retDict1["need"]
    needTree["leafA"]=retDict1["needTree"]
  #2.检测Stms
  retDict2=testStms(retDict1["new"])
  if not retDict2["status"]:
    return {"status":0}
  else:
    need=need+retDict2["need"]+"}"
    needTree["leafB"]=retDict2["needTree"]
    needTree["root"]=need+"  => Block"
    return {"status":1,"need":need,"new":retDict2["new"],"needTree":needTree}


def testWhile(code):
  need=""
  line=""
  stm=""
  needTree={"root":None}
  #读取一个语法单位
  retDict1=getCodeUnit(code)
  #由testSTM可知,这个语法单位绝对不可能出现空或者错误
  #1.头部分析
  #1.1根据语句构型来分割while头部 while(REL)
    #1.1是一条语句
  if retDict1["status"]==1:
    head=retDict1["need"].replace(" ","").replace("\n","").replace("\r","").replace("\t","")
    #1.2是一个语句块  
  elif retDict1["status"]==2:
    head=retDict1["before"]
  #1.2头部检测
  #1.2.1 检测开头是否存在while
  if head[:5]!="while" and len(head)>6:
    return {"status":0}
  #1.2.2 获取括号中的条件表达式
  retDict2=extractBreaket(head[5:])
  if not retDict2["status"]:
    return {"status":0}
  #1.2.3 解析条件表达式
  retDict3=testRel(retDict2["need"])
  if not retDict3["status"]:
    return {"status":0}
  needTree["leafA"]=head[:5]
  needTree["leafB"]=retDict3["needTree"]
  need=head[:5]+"("+retDict3["need"]+")"
  
  # print("ffff")
  #2.躯干STM分析
  #2.1 提取STM
  ## 若为块则取出块解析内容补上{}构成Blcok形作为STM,反之单条语句 则取出while(REL)后方的内容 同时补上;构成单条语句 
  stm="{"+retDict1["need"]+"}" if retDict1["status"]==2 else retDict2["new"]+";" 
  #2.2 STM检测
  retDict4=testStm(stm)
  if not retDict4["status"]:
    return {"status":0}
  else:
    need=need+retDict4["need"]
    needTree["leafC"]=retDict4["needTree"]
    needTree["root"]=need+"  => Stm_while"
    return {"status":1,"need":need,"new":retDict1["new"],"needTree":needTree}


def testIf(code):
  need=""
  line=""
  stm=""
  needTree={"root":None}
  #读取一个语法单位
  retDict1=getCodeUnit(code)
  #print("【if_retDict1】"+str(retDict1))
  #由testIfElse可知,这个语法单位绝对不可能出现空或者错误
  #1.头部分析
  #1.1根据语句构型来分割if头部 if(REL)
    #1.1是一条语句
  if retDict1["status"]==1:
    head=retDict1["need"].replace(" ","").replace("\n","").replace("\r","").replace("\t","")
    #1.2是一个语句块  
  elif retDict1["status"]==2:
    head=retDict1["before"]
  #1.2头部检测
  #1.2.1 检测开头是否存在if
  if head[:2]!="if" and len(head)>3:
    return {"status":0}
  #1.2.2 获取括号中的条件表达式
  retDict2=extractBreaket(head[2:])
  if not retDict2["status"]:
    return {"status":0}
  #1.2.3 解析条件表达式
  retDict3=testRel(retDict2["need"])
  if not retDict3["status"]:
    return {"status":0}
  need=head[:2]+"("+retDict3["need"]+")"
  needTree["leafA"]=head[:2]
  needTree["leafB"]=retDict3["needTree"]
  #2.躯干STM分析
  #2.1 提取STM
  ## 若为块则取出块解析内容补上{}构成Blcok形作为STM,反之单条语句 则取出if(REL)后方的内容 同时补上;构成单条语句 
  stm="{"+retDict1["need"]+"}" if retDict1["status"]==2 else retDict2["new"]+";" 
  #2.2 STM检测
  retDict4=testStm(stm)
  if not retDict4["status"]:
    return {"status":0}
  else:
    need=need+retDict4["need"]
    needTree["leafC"]=retDict4["needTree"]
    # needTree["root"]=need
    return {"status":1,"need":need,"new":retDict1["new"],"needTree":needTree}


def testElse(code):
  need=""
  line=""
  stm=""
  needTree={}
  #读取一个语法单位
  retDict1=getCodeUnit(code)
  #由testIfElse可知,这个语法单位绝对不可能出现空或者错误
  #1.头部分析
  #1.1根据语句构型来分割else头部 else
    #1.1是一条语句
  if retDict1["status"]==1:
    head=retDict1["need"].replace(" ","").replace("\n","").replace("\r","").replace("\t","")
    #1.2是一个语句块  
  elif retDict1["status"]==2:
    head=retDict1["before"]
  #1.2头部检测
  #1.2.1 检测开头是否存在else
  if head[:4]!="else" and len(head)>5:
    return {"status":0}
  need=head[:4]+" "
  needTree["leafA"]=head[:4]
  #2.躯干STM分析
  #2.1 提取STM
  ## 若为块则取出块解析内容补上{}构成Blcok形作为STM,反之单条语句 则取出else后方的内容 同时补上;构成单条语句 
  stm="{"+retDict1["need"]+"}" if retDict1["status"]==2 else head[4:]+";" 
  #2.2 STM检测
  retDict2=testStm(stm)
  if not retDict2["status"]:
    return {"status":0}
  else:
    need=need+retDict2["need"]
    needTree["leafB"]=retDict2["needTree"]
    return {"status":1,"need":need,"new":retDict1["new"],"needTree":needTree}


def testIfElse(code):
  need=""
  needTree={}
  #1.检测if
  retDict1=testIf(code)
  if not retDict1["status"]:
    return {"status":0}
  need=need+retDict1["need"]
  needTree=retDict1["needTree"]
  needTree["root"]=need
  #2.继续向后读一个语法单位 并判断其是否符合else判断
  retDict2=getCodeUnit(retDict1["new"])
    #若语法单位为错误或空 则返回if判定结果
  if retDict2["status"]==0 or retDict2["status"]==3:
    needTree["root"]=needTree["root"]+"  =>Stm_if"
    return {"status":1,"need":need,"new":retDict1["new"],"needTree":needTree}
  #3.反之则检测else
  retDict3=testElse(retDict1["new"])
  if not retDict3["status"]:
    needTree["root"]=needTree["root"]+"  =>Stm_if"
    return {"status":1,"need":need,"new":retDict1["new"],"needTree":needTree}
  else:
    need=need+retDict3["need"]
    needTree["root"]=need+"  =>Stm_if_else"
    needTree["leafD"]=retDict3["needTree"]["leafA"]
    needTree["leafE"]=retDict3["needTree"]["leafB"]
    return {"status":1,"need":need,"new":retDict3["new"],"needTree":needTree}



#testAssign()检测是否为赋值语句
#line为单行字段
def testAssign(line):
  need=""
  compents="" #保存赋值号左右两侧的元素
  needTree={"root":None}
  newline=line.replace(" ","").replace("\n","").replace("\r","").replace("\t","")
  #1.检测 是否有赋值号
  if "=" in newline:
    compents=newline.split("=")
  else:
    return {"status":0}
  needTree["leafB"]="=  => Op"
  #2.检测左侧是否为id
  retDict1=testName(compents[0])
  if not retDict1["status"]:
    return {"status":0}
  else:
    needTree["leafA"]=retDict1["needTree"]
    need=need+retDict1["need"]+"="
  #3.检测右侧是否为表达式
  retDict2=testExprBox(compents[1])
  if not retDict2["status"]:
    return {"status":0}
  else:
    need=need+retDict2["need"]+";"
    needTree["leafC"]=retDict2["needTree"]
    needTree["root"]=need+"  =>Stm_Assign"
  return {"status":1,"need":need,"needTree":needTree}



#testROp()检测line中是否包含关系运算符
#line 拟语句文本
def testROp(line):
  for op in ROp:
    if op in line:
      return {"status":1,"need":op,"new":line.split(op),"needTree":op+"  => ROp"}
  return {"status":0}

#testRel() 检测line中是否包含关系表达式
#line 拟条件文本
def testRel(line):
  needTree={"root":None}
  need=""
  newline=line.replace(" ","").replace("\n","").replace("\r","").replace("\t","")
  #1.检测运算符
  retDict1=testROp(newline)
  if not retDict1["status"]:
    return {"status":0}
  else:
    needTree["leafB"]=retDict1["needTree"]
    need=" "+retDict1["need"]+" "
  compents=retDict1["new"]
  #2.左侧表达式检测
  retDict2=testExprBox(compents[0])
  if not retDict2["status"]:
    return {"status":0}
  else:
    needTree["leafA"]=retDict2["needTree"]
    need=retDict2["need"]+need
  #3.右侧表达式检测
  retDict3=testExprBox(compents[1])
  if not retDict3["status"]:
    return {"status":0}
  else:
    need=need+retDict3["need"]
    needTree["leafC"]=retDict3["needTree"]
    needTree["root"]=need+"  => Rel"
    return {"status":1,"need":need,"needTree":needTree}




#继续向前获得一个语法单元
#我们认为语法单元分为两种
#{}包含的block块语句 以及;分割的行语句 line
#返回结果字典
#  {
#     "status":(0不存在|1是单行|2是block|3代码正常结束),
#     "before":前方文本(为block时存在|None)
#     "need":当前解析出的语法单位内容 (block则是里面的内容 单行则是去掉一个;的) 
#     "new":后方文本(该单位后方的文本|None)
#   }
def getCodeUnit(code):
  for index,char in enumerate(code):
    #1.碰到block
    if char =="{":
      retDict1=extractBlock(code[index:])
      #1.1 block异常
      if not retDict1["status"]:
        return {"status":0}
      else:
      #1.2 block正常 返回block中前方代码 block中的代码 以及后续的代码  
        return {"status":2,"before":code[:index].strip().replace("\n","").replace("\r","").replace("\t",""),"need":retDict1["need"],"new":retDict1["new"]}
    #2.碰到单行语句
      #2.1
    if char==";":
      line=code[:index].strip().replace("\n","").replace("\r","").replace("\t","")
      return {"status":1,"before":None,"need":code[:index],"new":code[index+1:]}
  
  #3.没有触发block或者语句结束
  tmpCode=code.replace(" ","").replace("\n","").replace("\r","").replace("\t","")
  #3.1 若此时的code除了特殊符号外无其他内容 则说明正常结束
  if len(tmpCode)==0:
    return {"status":3}
  #3.2 若包含其他字符内容 此时结束不正常
  else:
    return {"status":0}



def testFullCode(code):
  #获取一个语法单元
  retDict1=getCodeUnit(code)
  #若该单元是Block 
  if retDict1["status"]==2:
    retDict2=testBlock(retDict1["need"])
    if not retDict2["status"]:
      print("error")
      return None
    else:
      return retDict2["needTree"]
  #若首个语法单元不是Block 报错
  else:
    print("error!")
    return None


      
if __name__ == "__main__":
  # fullExpr=input("请输入一段表达式字符(回车确认):")
  # MainTree=testFullExpr(fullExpr)
  # if MainTree is not None:
  #   drawTree(MainTree)

  fileName=input("请输入代码文件名:")
  code=""
  with open(fileName,'r') as f:
    code=f.read()
  MainTree=testFullCode(code)
  if MainTree is not None:
    print("完成解析,开始绘图....")
    drawTree(MainTree)
    print("绘图完成")